import { newArrayProto } from "./array";

class Observe {
  constructor(data) {
    /**
     * 下面会发生死循环，解决思路：1. 变为不可枚举; 2. 放到 walk 之后; 3. walk 时过滤 __ 开头的属性
     */
    // data.__ob__ = this;
    Object.defineProperty(data, "__ob__", {
      enumerable: false, // 变为不可枚举，循环时候无法访问到
      configurable: false,
      value: this,
    });
    // Object.defineProperty 只能劫持已经存在的属性
    // vue2 单独为此写一些 api: $set $delete
    if (Array.isArray(data)) {
      // 重写数组中的方法
      this.observeArray(data);
    } else {
      this.walk(data);
    }
  }
  /** 循环对象，对属性依次劫持 */
  walk(data) {
    Object.keys(data).forEach((key) => {
      // if (!key.startsWith("__")) {
      // “重新定义” 属性(性能差)
      defineReactive(data, key);
      // }
    });
  }
  observeArray(data) {
    data.__proto__ = newArrayProto;
    data.forEach((item) => {
      observe(item);
    });
  }
}

export function defineReactive(data, key) {
  let val = data[key];
  observe(val);
  // debugger;
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      //   console.log(`获取 ${key} 被监听`);
      return val;
    },
    set(newValue) {
      if (newValue === val) return;
      observe(newValue);
      val = newValue;
      console.log(
        `设置${key}已经被监听了，现在值为：`,
        JSON.stringify(newValue)
      );
    },
  });
}

/** 将 data 对象进行数据劫持 */

export function observe(data) {
  // 对这个对象进行劫持
  if (typeof data !== "object" || data === null) return;
  if (data.__ob__ instanceof Observe) {
    return data.__ob__;
  }
  // 只对对象进行劫持
  // 如果一个对象被劫持过了，不需要再被劫持了
  // 要判断一个对象是否被劫持过，可以添加一个实例，用实例来判断是否被劫持过
  return new Observe(data);
}
